#!/usr/bin/env python

import gzip
import os

elisp_info_file="/usr/share/info/elisp.info.gz"
auto_builtin_funcs="auto_builtin_funcs.go"
auto_builtin_macros="auto_builtin_macros.go"

funcfp = open(auto_builtin_funcs, "w")
funcfp.truncate()
funcfp.writelines("// auto gennerated, not modify\n")
funcfp.writelines("package goem\n\n")
funcfp.writelines("//aa\n")

macrofp = open(auto_builtin_macros, "w")
macrofp.truncate()
macrofp.writelines("// auto gennerated, not modify\n")
macrofp.writelines("package goem\n\n")
macrofp.writelines("//aa\n")

gz = gzip.open(elisp_info_file)


def keyword_fmt(word):
    p = word
    fmtp = p.replace('-', '_')
    if fmtp == 'type': fmtp = 'type_'
    if fmtp == 'range': fmtp = 'range_'
    if fmtp == 'func': fmtp = 'func_'
    if fmtp == 'default': fmtp = 'default_'
    if fmtp == 'map': fmtp = 'map_'
    if fmtp == 'struct': fmtp = 'struct_'
    if fmtp == 'var': fmtp = 'var_'
    return fmtp


def func_name_fmt(name):
    fname = name[0].upper() + name[1:]
    fname = fname.replace('-', '_')
    if fname == "=": fname = "Equal1"
    if fname == "/=": fname = "DivAssign"
    if fname == "<": fname = "LittleThan"
    if fname == "<=": fname = "LittleOrEqual"
    if fname == ">": fname = "GreaterThan"
    if fname == ">=": fname = "GreaterOrEqual"
    if fname == "+": fname = "Plus"
    if fname == "-": fname = "Sub"
    if fname == "*": fname = "Cheng"
    if fname == "/": fname = "Div"
    if fname == "%": fname = "Mod1"
    if fname == "1+": fname = "OnePlus"
    if fname == "_": fname = "UnderLine"
    if fname == "1_": fname = "OneUnderLine"
    if fname == "String=": fname = "StringEqual"
    if fname == "String<": fname = "StringLittleThan"
    if fname == "Smie_prec2_>grammar": fname = "Smie_prec2_gt_grammar"
    if fname == "Smie_bnf_>prec2": fname = "Smie_bnf_gt_prec2"
    if fname == "Smie_precs_>prec2": fname = "Smie_precs_gt_prec2"
    if fname == "1value": fname = "Onevalue"
    return fname


def gen_func_params_part(parts):
    if True:
        params = []
        has_rests = False
        has_optionals = False
        for p in parts[3:]:
            if p == "&optional":
                prmline = 'optionals ... Value'
                params.append(prmline)
                # callargs.append('optionals ...')
                has_optionals = True
                break
            elif p == "&rest":
                prmline = 'rests ... Value'
                params.append(prmline)
                has_rests = True
                break
            else:
                if p == 'symbol':
                    # prmline = '%s string' % (p)
                    # params.append(prmline)
                    prmline = '%s Value' % (p)
                    params.append(prmline)
                else:
                    fmtp = keyword_fmt(p)
                    prmline = '%s Value' % (fmtp)
                    params.append(prmline)
        # print(', '.join(params))
        params_line = ', '.join(params)
    return params_line


def gen_func_callargs_part(parts):
    if True:
        callargs = []
        has_rests = False
        has_optionals = False
        code = ''
        for p in parts[3:]:
            if p == "&optional":
                has_optionals = True
                break
            elif p == "&rest":
                has_rests = True
                break
            else:
                if p == 'symbol':
                    # code += 'symv := emrt.env.Intern(symbol)\n'
                    # callargs.append('symv')
                    callargs.append(p)
                else:
                    fmtp = keyword_fmt(p)
                    callargs.append(fmtp)
        callargs_line = ', '.join(callargs)
        code += 'args_all := []Value{%s}\n' % (callargs_line)
        if has_rests:
            code += 'args_all = append(args_all, rests...)\n'
        if has_optionals:
            code += 'args_all = append(args_all, optionals...)\n'
    return code


funcs = {}

func_total = 0
func_gened = 0
# TODO auto symbol param from string
def process_func(line):
    if True:
        global func_total, func_gened
        func_total += 1

        fp = funcfp
        parts = line.split(' ')
        # print(parts)
        fname = func_name_fmt(parts[2])
        if fname in funcs: return

        func_gened += 1
        funcs[fname] = True
        params_line = gen_func_params_part(parts)
        code = 'func %s(%s) Value {\n' % (fname, params_line)
        if code is not None:
            funcfp.write(code)
        code = gen_func_callargs_part(parts)
        fp.write(code)
        callargs_line = '"%s", args_all' % (parts[2])
        code = 'return emrt.env.Funcall0(%s)\n}' % (callargs_line)
        fp.write(code + "\n")
        fp.write("\n")
    return


macro_total = 0
macro_gened = 0
def process_macro(line):
    if True:
        global macro_total, macro_gened
        macro_total += 1

        fp = macrofp
        parts = line.split(' ')
        # print(parts)
        fname = func_name_fmt(parts[2])
        if fname in funcs: return
        for p in parts[3:]:
            if p.find('(') != -1 or p.find('...') != -1 or p.find('[') != -1:
                print("formed macro:", p)
                return

        macro_gened += 1
        funcs[fname] = True
        params_line = gen_func_params_part(parts)
        code = 'func %s(%s) Value {\n' % (fname, params_line)
        if code is not None:
            fp.write(code)
        code = gen_func_callargs_part(parts)
        fp.write(code)
        callargs_line = '"%s", args_all...' % (parts[2])
        code = 'return builtin_call(%s)\n}' % (callargs_line)
        fp.write(code + "\n")
        fp.write("\n")
    return


varfp = open("auto_builtin_vars.go", "w")
varfp.truncate()
varfp.write("// auto gennerated, not modify\n")
varfp.write("package goem\n\n")
varsetlines = ''

varfp_t = open("tests/auto_builtin_vars_tests.go", "w")
varfp_t.truncate()
varfp_t.write("// auto gennerated, not modify\n")
varfp_t.write("package main\n\n")
varfp_t.write('import ("fmt")\n')
varfp_t.write('import ("go-emacs-module")\n\n')
varfp_t.write("func test_auto_buildtin_vars() {\n")


# just print it
def process_variable_tests(varname, varname_raw):
    fp = varfp_t
    fp.write('    fmt.Println(goem.%s, "%s:", ev2go(goem.%s)) // %s\n' %
             (varname, varname_raw, varname, varname_raw))
    return


def process_variable(line):
    if True:
        parts = line.split(' ')
        # print(parts)
        fname = parts[2][0].upper() + parts[2][1:]
        fname = fname.replace('-', '_')
        fname = func_name_fmt(parts[2])
        if fname == 'Buffer_file_name': fname = fname + 'v'

        funcs[fname] = True
        codeline = 'var %s Value' % (fname)
        # print(codeline)
        varfp.write(codeline + "\n")

        codeline = '    %s = emrt.env.Intern("%s")\n' % (fname, parts[2])
        # varfp.write(codeline + "\n")
        global varsetlines
        varsetlines = varsetlines + codeline + "\n"

        process_variable_tests(fname, parts[2])
    return


while True:
    line = gz.readline()
    if line is None: break
    if line == b'': break

    line = line.decode().strip()
    # print(line)
    if line.startswith('-- Function: '):
        # print(line)
        process_func(line)
        True
    elif line.startswith('-- Macro: '):
        process_macro(line)
        True
    elif line.startswith('-- Variable: '):
        process_variable(line)
        True


varfp.write("\n\n")
varfp.write("func set_elisp_vars_value() {\n")
varfp.write(varsetlines)
varfp.write("}\n")
varfp.close()

varfp_t.write("}\n")
varfp_t.close()

macrofp.write('// macros: total: %d, gened: %d, drop: %d\n' %
              (macro_total, macro_gened, macro_total - macro_gened))
macrofp.close()

funcfp.write('// funcs: total: %d, gened: %d, drop: %d\n' %
             (func_total, func_gened, func_total - func_gened))
funcfp.close()

gz.close()
